package com.test.login_lejingling_cjl.interceptors;


import com.alibaba.fastjson.JSONObject;
import com.test.login_lejingling_cjl.dto.Result;
import com.test.login_lejingling_cjl.utils.IPUtil;
import com.test.login_lejingling_cjl.utils.JwtUtils;
import com.test.login_lejingling_cjl.utils.RedisOperator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
//拦截器 另一种filter
public class LoginCheckIntercepter implements HandlerInterceptor {
    @Autowired
    RedisOperator redis;

    @Value("${blackIp.continueCounts}")
    private Integer continueCounts;

    @Value("${blackIp.timeInterval}")
    private Integer timeInterval;

    @Value("${blackIp.limitTimes}")
    private Integer limitTimes;
    //目标资源运行前运行，true运行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url=request.getRequestURL().toString();
        //ip黑名单
        String ip = IPUtil.getIP((ServerHttpRequest) request);
        if(!doLimit(ip)) return false;

        log.info("请求路径为：{}",url);
        if(url.contains("login")){
            return true;
        }

        //判断令牌是否存在
        String jwt=request.getHeader("token");
        //使用的是springFramwork的工具包
        if(!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录的信息");
            Result error= Result.error("NOT_LOGIN");
            //因为不是controler层无法自动将结果封装成json格式数据，手动转换  阿里巴巴fastJson
            String notlogin= JSONObject.toJSONString(error);
            //返回给浏览器
            response.getWriter().write(notlogin);
            return false;
        }
        //解析令牌
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            log.info("解析失败");
            Result error=Result.error("NOT_LOGIN");
            //因为不是controler层无法自动将结果封装成json格式数据，手动转换  阿里巴巴fastJson
            String notlogin= JSONObject.toJSONString(error);
            //返回给浏览器
            response.getWriter().write(notlogin);
            throw new RuntimeException(e);
        }
        //令牌合法
        log.info("放行");
        return true;
    }
    //标资源方法运行后运行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    //试图渲染完毕后运行，最后运行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
    public  boolean doLimit(String ip) {
        // 正常的ip定义
        final String ipRedisKey = "ip:" + ip;
        // 被拦截的黑名单ip，如果在redis中存在，则表示目前被关小黑屋
        final String ipRedisLimitKey = "ip:limit:" + ip;

        // 获得当前的ip并且查询还剩下多少时间，如果时间存在（大于0），则表示当前仍然处在黑名单中
        long limitLeftTimes = redis.ttl(ipRedisLimitKey);
        if (limitLeftTimes > 0) {
            // 终止请求，返回错误
            return false;
        }

        // 在redis中获得ip的累加次数
        long requestCounts = redis.increment(ipRedisKey, 1);
        /**
         * 判断如果是第一次进来，也就是从0开始计数，则初期访问就是1，
         * 需要设置间隔的时间，也就是连续请求的次数的间隔时间
         */
        if (requestCounts == 1) {
            redis.expire(ipRedisKey, timeInterval);
        }

        /**
         * 如果还能获得请求的正常次数，说明用户的连续请求落在限定的[timeInterval]之内
         * 一旦请求次数超过限定的连续访问次数[continueCounts]，则需要限制当前的ip
         */
        if (requestCounts > continueCounts) {
            // 限制ip访问的时间[limitTimes]
            redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);
            // 终止请求，返回错误
            return false;
        }
        return true;
    }
}
